/************************************************************************
 * SDL_Layer - A layered display mechanism for SDL
 * Copyright (C) 2008-2011  Julien CLEMENT (Andematunum Software)
 *
 * PROJECT HOME : http://code.google.com/p/sdl-layer/
 * MAILING LIST : http://groups.google.com/group/sdl-layer-group
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ************************************************************************/

#ifndef _SDL_Layer_h_
#define _SDL_Layer_h_

#include "SDL.h"

#include "begin_code.h"
#include "SDL_Layer_structs.h"

#ifdef __cplusplus
extern "C" {
#endif

/** @file SDL_Layer.h
 *  @author Andematunum Software (andematunum@gmail.com)
 *  @brief A generic system to manage multiple blit layers <br/>
 *         with the SDL library (http://www.libsdl.org)
 *  @note The refresh system supports 2 refresh methods : <br/>
 - Dirty rectangles method (SDL_UpdateRects) <br/>
 - Whole refresh (SDL_Flip) <br/>
 *  @note Comments, suggestions, bugs are welcomed. <br/>
 */

/** Refresh method to use */
#define SDLAYER_RECTS (0x1) /**< Dirty rectangles */
#define SDLAYER_FLIP  (0x0) /**< Flip (SDL_Flip)  */

/** When using a dirty rectangle method, we avoid frequent calls to malloc <br/>
 *  by managing memory "chunks". <br/>
 *  This constant specifies the size of those memory chunks. <br/>
 *  <br/>
 *  Default value: 128
 */
#define SDLAYER_CHUNK_SIZE  ( 128 )

/** [EXPERIMENTAL] Distance to decide if we should merge 2 rectangles or not <br/>
 * The default value (8) has been established experimentally. <br/>
 * Merging rectangles slightly improves the FPS (~ +20 frames) <br/>
 * You can desactivate it by commenting out the SDLAYER_USE_MERGE macro.
 */
#define SDLAYER_USE_MERGE
#define SDLAYER_MERGE_DISTANCE  ( 8 )

/** Create a new LayeredDisplay with each surface beeing allocated and <br/>
 *  converted into the display format. <br/>
 *  <br/>
 *  Parameters are exactly the same as the ones of SDL_CreateRGBSurface. <br/>
 *  See the SDL documentation for more information. <br/>
 *  <br/>
 *  @param flags SDL flags
 *  @param refresh_method : <br/>
 *          - SDLAYER_FLIP (use SDL_Flip for refreshing the whole display) <br/>
 *          - SDLAYER_RECTS (use SDL_UpdateRects for updating the dirty <br/>
 *            rectangle)
 *  @param n_layers  Number of layers of the layered display
 *  @param width   Width of each surface/layer
 *  @param height    Height of each surface/layer
 *  @param bitsPerPixel  number of bits per pixel for each surface/layer
 *  @param Rmask,Gmask,Bmask,Amask   RGBA masks for each surface/layer
 *  @return A newly allocated layered display
 *
 *  @see  SDLayer_FreeLayeredDisplay
 *  @see  SDL_CreateRGBSurface
 */
DECLSPEC SDLayer_Display*
SDLayer_CreateRGBLayeredDisplay(Uint32 flags, int refresh_method, int n_layers,
		int* widths, int* heights, int bitsPerPixel, Uint32 Rmask,
		Uint32 Gmask, Uint32 Bmask, Uint32 Amask);

/** Sets a global color key value for all the layers of a layered display.
 *
 *  @param layered_display The layered display to which we set the global colorkey
 *  @param flags SDL Flags passed to SDL_SetColorKey
 *  @param key Value of the color used as a transparent value
 *
 *  @see  SDL_SetColorKey
 */
DECLSPEC void SDLayer_SetColorKey(SDLayer_Display* layered_display,
		Uint32 flags, Uint32 key);

/** Get a pointer to the layer at <i>layer_number</i>. <br/>
 *  Useful to perform custom operations on a particular layer other than a simple blit. <br/>
 *  For instance, modifying the Alpha value or drawing primitives through <br/>
 *  direct access to the pixel data.<br/>
 *  Beware of the fact that any operation on the pixels which doesn't <br/>
 *  use SDLayer_Blit function will not benefit from the refresh management <br/>
 *  system of the library.
 *  @param layered_display The layered display to get a surface/layer from
 *  @param layer_number Number of the layer to get
 *  @return A pointer to the layer at <i>layer_number</i>
 */
DECLSPEC SDL_Surface* SDLayer_GetLayer(SDLayer_Display* layered_display,
		int layer_number);

/** Frees the memory allocated for the layered display.
 *  It internally calls SDL_FreeSurface on each layer surface.
 *
 *  @param  layered_display   The layered display to free
 */
DECLSPEC void SDLayer_FreeLayeredDisplay(SDLayer_Display* layered_display);

/** Blits a surface onto a particular layer of the layered display.
 *  This function also performs various operations like adding dirty rectangles
 *  (if needed), clipping, and more.
 *
 *  @param src Surface to blit from
 *  @param srcrect Area of the source surface to blit
 *  @param layered_display Layered display to blit to
 *  @param dstrect Area of the layered display to which the blit is performed
 *  @param layer_numberLayer number on which the blit will be performed
 *
 *  @see SDLayer_Update
 *  @see SDL_BlitSurface
 */
DECLSPEC void SDLayer_Blit(SDL_Surface* src, SDL_Rect* srcrect,
		SDLayer_Display* layered_display, SDL_Rect* dstrect, int layer_number);

/** Make a blit of all the layers of the layered display on the <br/>
 * display surface (screen), beginning from the lowest layer <br/>
 * (layer at indice 0). <br/>
 *  Then, update the screen according to the selected refresh method (dirty rectangles or flip).
 *
 *  @param layered_display The layered display to blit on the screen
 *
 *  @see SDL_UpdateRects
 *  @see SDL_Flip
 */
DECLSPEC void SDLayer_Update(SDLayer_Display* layered_display);

/** Set the "force_whole_refresh" flag to one so that the next call to
 * SDL_UpdateLayeredDisplay performs a whole refresh of the display,
 * even if there're pending dirty rectangles to update.
 * If any, there will be discarded.
 */
DECLSPEC void SDLayer_ForceWholeRefresh(SDLayer_Display* layered_display);

/** Fill each layer with an uniform color.
 *
 *  @param  layered_display   The layered display to fill with color
 *  @param  color     The color used to fill each surface/layer
 *
 *  @see  SDL_FillRect
 */
DECLSPEC void SDLayer_Fill(SDLayer_Display* layered_display, Uint32 color);

/** Toggle whether or not a given layer is show on the screen.
 *
 *  @param  layered_display   The layered display
 *  @param  layer_number    Number of the layer to change the visilibity
 *  @param  toggle      1: show layer, 0: hide layer
 *
 *  @note If any change is provided to the layer, this function will trigger a whole refresh
 *      of the display.
 *  @note By default, all the layers are visible.
 */
DECLSPEC void SDLayer_SetVisible(SDLayer_Display* layered_display,
		int layer_number, int toggle);

/** Change the scrolling factor of a given layer.
 * Useful only when the layer size is bigger than the display size.
 * It is represented with a single precision floating point number.
 */
DECLSPEC void SDLayer_SetScrollingFactor(SDLayer_Display* layered_display,
		int layer_number, float scrolling_factor);

/** Changes the current position of the viewport.
 * Note that the coordinates can go beyond a layer's size, including negative coordinates.
 * The blit will then be clipped to the visible part of the layers.
 */
DECLSPEC void SDLayer_SetViewport(SDLayer_Display* layered_display, Sint16 x,
		Sint16 y);

/** Changes the display rectangle. Useful when you want to project the layers
 * to a particular area of the screen and not to the whole screen.
 * It is used in games having menu bars separated from the game area, for instance.
 */
DECLSPEC void SDLayer_SetDisplayRect(SDLayer_Display* layered_display,
		SDL_Rect* rect);

/** Restore the display rectangle as the whole screen in case you changed it
 *  using SDLayer_SetViewport.
 */
DECLSPEC void SDLayer_UnsetDisplayRect(SDLayer_Display* layered_display);

/** Defines a region on a layer. A region is a rectangular area of a layer for which it is known
 *  that nothing will never be blit outside of it. It is made for accelerating blits.
 */
DECLSPEC void SDLayer_SetRegion(SDLayer_Display* layered_display,
		int layer_number, SDL_Rect* region);

/** Remove a region previously set on a layer by SDLayer_SetRegion */
DECLSPEC void SDLayer_UnsetRegion(SDLayer_Display* layered_display,
		int layer_number);

/** A replacement to SDL_WM_ToggleFullScreen.
 *  This function is supposed to work on all systems. It simply makes a copy of the current
 *  screen and attempt to switch to fullscreen mode by calling SDL_SetVideoMode another
 *  time.
 *  In case of failure, the program remains in windowed mode.
 *  If already in fullscreen mode, switch back to windowed mode.
 */
DECLSPEC void SDLayer_ToggleFullScreen(SDLayer_Display* layered_display);

/** Given screen coordinates, get the absolute coordinates in the specified layer.
 *  Useful to transform mouse coordinates into layer coordinates to know which object
 *  has been selected, for instance.
 */
DECLSPEC void SDLayer_GetLayerCoordinates(SDLayer_Display* ld, Sint16* layer_x,
		Sint16* layer_y, Sint16 x, Sint16 y, int layer);

/** Add a blit rectangle.
 *  Call that function whenever you make a direct blit
 *  on a layer, using SDLayer_GetLayer() and then SDL_BlitSurface() on it.
 *  If you forget, you may have rectangles which don't get refreshed
 *  properly.
 */
DECLSPEC void SDLayer_AddRect(SDLayer_Display* ldisplay, SDL_Rect* dstrect,
		int layer_number);

#ifdef __cplusplus
}
#endif

#endif

